# -*- coding: utf-8 -*-

import cgi
import datetime
import wsgiref.handlers

from google.appengine.ext import db
from google.appengine.ext.db import polymodel


class VersionLeech( db.Model ):
    '''We use this model to leech on system-based creation of unique identifiers'''
    ident = db.IntegerProperty()

    @staticmethod
    def getNewKey():
        old = VersionLeech.all().order('-ident').get()
        if old == None:
            old = VersionLeech(ident = 0)
            old.put()
        newId = old.ident+1
        VersionLeech(ident = newId).put()
        return newId

    @staticmethod
    def purgeOldEntities():
        res = ''
        first = VersionLeech.getNewKey()
        while True:
            for entity in VersionLeech.all().order('ident'):
                if entity.ident == first:
                    return res
                else:
                    res += 'Deleting:'
                    res += str(entity)
                    res += '<br />\n'
                    entity.delete()
        

class EnhancedModel( db.Model ):
    '''EnhancedModel is a base class for all models that should have a version.
    After inheritage user should override __prefix__ field preferably with class name.
    '''
    # do NOT uncomment the following line:
    # __prefix__ = 'EnhancedModel'
    # it is merely a reminding tha all classes that inherits EnhancedModel
    # should specify __prefix__ field in proper way (ie. should be alpha-numeric strings)

    version = db.IntegerProperty(default=0)
    ident = db.IntegerProperty(required=True)
    archival = db.BooleanProperty(default=False)

    def linkTo( self, text = None, action = 'show1', archival=False, link=None ):
        
        defaultText = {"show1" : "[Show]",
                       "showAll" : "[Show all]",
                       "edit" : "[Edit]",
                       "delete" : "[Delete]",
                       "new": "[New]"
                       }
        
        linkFunction = {"show1" : "showLink",
                       "showAll" : "showAllLink",
                       "edit" : "editLink",
                       "delete" : "deleteLink",
                       "new" : "newLink"
                       }
        
        if (not text) and (action in defaultText):
            text = defaultText[action]
        
        if not link:
            link = eval('self.'+linkFunction[action]+'()')
            # oczekujemy, że przy braku linka akcja będzie mieć przypisaną funkcję generującą link
        
        if not text:
            raise Exception("No text specified")

        options = ""
        if archival:
            options += "archival=True&"

        if options:
            options = "?" + options
        
        return '''<a href="%s%s">%s</a>''' % ( link, options, text )
        
    def showGeneric(self):
        fieldsRepr = "\n<br />".join( [ cgi.escape('''%s = %s''' % ( fieldName.capitalize(), (eval('''self.%s''' % fieldName)) )) for fieldName in self.fields() if fieldName not in ["_class"] ] )
        br = "<br />\n"
        
        # key = self.key()
        # str_key = str(key) + " || " + str(key.id()) + " || " + repr(key.name())
        # str_key = br + str_key + br

        return ('<b>' + self.linkTo(action='edit') + self.linkTo(action='delete') + "</b>" + br + fieldsRepr)

    def editGeneric(self):
        preview = '<br /><font color=blue>Object preview:</font>\n<br />\n<br />' + self.showGeneric() + "\n<hr>"
        pre = '''<form action="./%d" method=post>''' % (self.ident,)
        row = '''<div>%s: <input type="text" maxlength="100" name="%s" id="%s" value="%s"></div>'''
        post = '''
              <input type="submit" value="Send" />
              </form>'''

        # bo CLEAR jest ZŁY:               <input type="reset" value="Clear" />

        middle = "\n<br />".join(
            [ row % ( fieldName.capitalize(), fieldName, fieldName.capitalize(), eval('''self.%s''' % fieldName) )
              for fieldName in self.fields()
              if fieldName not in ["ident","version","archival","_class"]]
            )
        
        return preview + pre + middle + post

    def deleteGeneric(self):
        preview = '<br /><font color=blue>Object preview:</font>\n<br />\n<br />' + self.showGeneric() + "\n<hr>"
        pre = '''<form action="./%d" method=post>''' % (self.ident,)
        middle = '''<div>Are you sure to delete this entry?
        <LABEL><input type="radio" name="delete" value="YES" CHECKED/>YES</LABEL>
        <LABEL><input type="radio" name="delete" value="NO" />NO</LABEL>'''

        post ='''<input type="submit" value="Send" />
              </form>'''

        return preview + pre + middle + post

    # Following methods should return url for performing action (like editing) on object instance
    # or raise an exception if this action is not implemented.

    def editLink(self):
        raise Exception('Unsupported method or not yet implemented!')

    def newLink(self):
        raise Exception('Unsupported method or not yet implemented!')

    def deleteLink(self):
        raise Exception('Unsupported method or not yet implemented!')

    def showLink(self):
        raise Exception('Unsupported method or not yet implemented!')

    def showAllLink(self):
        raise Exception('Unsupported method or not yet implemented!')
        

    @staticmethod
    def queryLatest(classDef,ident=None):
        '''Queries for the latest version of model. Skips archival versions. If ident is given, we narrow our query.'''
        result = EnhancedModel.queryAll( classDef, ident ).filter('archival =', False)
        return result
        
    @staticmethod
    def queryAll(classDef,ident=None):
        '''Queries for all versions of given model. If ident is not None, we narrow our query to given ident.'''

        query = classDef.all().order('-version')
        if ident:
            query.filter('ident =',ident)

        return query
    
    @staticmethod
    def deprecateOldVersion( new, old ):
        '''After creating new version of an object, use this method to fetch version number and key_name from an old one.
        Old object is marked as "archival". Version number for new object is incremented.
        Make sure there is no more recent object than old or shitstorm will happen.

        Both objects are automatically put().
        '''

        new.version = old.version + 1
        new.put()
        old.deleteInstance()
        
    def deleteInstance(self):
        self.archival = True
        self.put()
    
    def __init__(self, *args, **kw):
        # If we initialize an object without setting it's key_name
        # system will automatically pick a name for us. we can then use it's
        # key_name to generate our own's
        key = VersionLeech().getNewKey()

        if 'ident' not in kw:
            kw['ident'] = key

        # Insert the new key_name property into the keyword arguments
        kw['key_name'] = 'key-' + self.__prefix__ + '-' + str(key)

        # Call the constructor of the base class
        db.Model.__init__(self, *args, **kw)
